package org.study.minio.boot.config;

import com.alibaba.fastjson2.JSON;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.*;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.*;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.util.IOUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URL;
import java.util.*;

/**
 * aws-s3通用存储操作，支持所有兼容s3协议的云存储：阿里云OSS、腾讯云COS、七牛云、京东云、minio等
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年10月13日
 */
@Slf4j
@RequiredArgsConstructor
@Component
@EnableConfigurationProperties(AwsProperties.class)
public class AwsTemplate implements InitializingBean {
    private final AwsProperties awsProperties;
    private AmazonS3 amazonS3;

    @Override
    public void afterPropertiesSet() {
        ClientConfiguration clientConfig = new ClientConfiguration();
        // 默认HTTPS
        clientConfig.setProtocol(Protocol.HTTP);
        // clientConfig.setMaxConnections(ClientConfiguration.DEFAULT_MAX_CONNECTIONS);
        clientConfig.setMaxConnections(awsProperties.getMaxConnections());
        clientConfig.setConnectionTimeout(ClientConfiguration.DEFAULT_CONNECTION_TIMEOUT);
        // v2-S3SignerType，v4-AWSS3V4SignerType
        clientConfig.setSignerOverride("S3SignerType");

        AWSCredentials credentials = new BasicAWSCredentials(awsProperties.getAccessKey(), awsProperties.getSecretKey());
        AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);

        // AwsClientBuilder.EndpointConfiguration endpointConfig = new AwsClientBuilder.EndpointConfiguration(awsProperties.getEndpoint(), null);
        // AwsClientBuilder.EndpointConfiguration endpointConfig = new AwsClientBuilder.EndpointConfiguration(awsProperties.getEndpoint(), awsProperties.getRegion());
        AwsClientBuilder.EndpointConfiguration endpointConfig = new AwsClientBuilder.EndpointConfiguration(awsProperties.getEndpoint(), Regions.CN_NORTH_1.name());

        AmazonS3 amazonS31 = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfig)
                .withClientConfiguration(clientConfig)
                .withCredentials(credentialsProvider)
                .withPathStyleAccessEnabled(awsProperties.getPathStyleAccessEnabled())
                .disableChunkedEncoding()
                .build();

        AmazonS3 amazonS32 = AmazonS3ClientBuilder.standard()
                .withEndpointConfiguration(endpointConfig)
                .withClientConfiguration(clientConfig)
                .withCredentials(credentialsProvider)
                .withPathStyleAccessEnabled(awsProperties.getPathStyleAccessEnabled())
                .withChunkedEncodingDisabled(true)
                .build();

        this.amazonS3 = amazonS31;
    }

    /**
     * 判断Bucket是否存在：true-存在、false-不存在
     *
     * @param bucketName 桶名
     */
    public boolean bucketExists(String bucketName) {
        return amazonS3.doesBucketExistV2(bucketName);
    }

    /**
     * 创建桶
     *
     * @param bucketName 桶名
     */
    public boolean createBucket(String bucketName) {
        if (bucketExists(bucketName)) {
            return true;
        } else {
            Bucket bucket = amazonS3.createBucket(bucketName);
            log.info("[Aws工具类]创建bucket={}成功", bucketName, JSON.toJSONString(bucket));
            return true;
        }
    }

    /**
     * 获取Bucket的策略
     *
     * @param bucketName 桶名
     */
    public String getBucketPolicy(String bucketName) {
        if (!bucketExists(bucketName)) {
            return null;
        }
        BucketPolicy bucketPolicy = amazonS3.getBucketPolicy(bucketName);
        String policyText = bucketPolicy.getPolicyText();
        return policyText;
    }

    /**
     * 获取所有Bucket列表
     */
    public List<Bucket> getAllBuckets() {
        return amazonS3.listBuckets();
    }

    /**
     * 根据bucketName获取其相关信息
     *
     * @param bucketName 桶名
     */
    public Optional<Bucket> getBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            return Optional.empty();
        }
        return getAllBuckets().stream().filter(b -> b.getName().equals(bucketName)).findFirst();
    }

    /**
     * 根据bucketName删除Bucket：true-删除成功、false-删除失败
     *
     * @param bucketName 桶名
     */
    public void removeBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            return;
        }
        amazonS3.deleteBucket(bucketName);
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return S3ObjectSummary 列表
     */
    public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
        return new ArrayList<>(objectListing.getObjectSummaries());
    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    过期时间，<=7
     * @return url
     */
    public String getObjectURL(String bucketName, String objectName, Integer expires) {
        Date date = new Date();
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, expires);
        URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime());
        return url.toString();
    }

    /**
     * 获取文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     */
    public S3Object getObject(String bucketName, String objectName) {
        return amazonS3.getObject(bucketName, objectName);
    }

    /**
     * 上传文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws IOException
     */
    public String putObject(String bucketName, String objectName, InputStream stream) throws IOException {
        String url = putObject(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream");
        return url;
    }

    /**
     * 上传文件
     *
     * @param bucketName  bucket名称
     * @param objectName  文件名称
     * @param stream      文件流
     * @param size        大小
     * @param contextType 类型
     * @throws IOException
     */
    public String putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws IOException {
        // String fileName = getFileName(objectName);
        byte[] bytes = IOUtils.toByteArray(stream);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(size);
        objectMetadata.setContentType(contextType);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        // 上传
        PutObjectResult putObjectResult = amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
        String url = awsProperties.getEndpoint() + "/" + bucketName + "/" + objectName;
        return url;
    }

    /**
     * 获取文件信息
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     */
    public S3Object getObjectInfo(String bucketName, String objectName) {
        return amazonS3.getObject(bucketName, objectName);
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     */
    public void removeObject(String bucketName, String objectName) {
        amazonS3.deleteObject(bucketName, objectName);
    }
}
